import backtrader as bt


# backtrader实现利弗莫尔金字塔式加仓
class LivermorePyramidingStrategy(bt.Strategy):
    params = (
        ("initial_size", 1),
        ("add_on_percentage", 0.5),
        ("stop_loss_percentage", 0.1),
    )

    def __init__(self):
        self.dataclose = self.datas[0].close
        self.order = None
        self.position_size = self.params.initial_size
        self.buy_price = None

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return

        if order.status in [order.Completed]:
            if order.isbuy():
                self.buy_price = order.executed.price
                self.log(
                    f"BUY EXECUTED, Price: {order.executed.price}, Cost: {order.executed.value}, Size: {order.executed.size}"
                )
            elif order.issell():
                self.log(
                    f"SELL EXECUTED, Price: {order.executed.price}, Cost: {order.executed.value}, Size: {order.executed.size}"
                )
            self.order = None

        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log("Order Canceled/Margin/Rejected")

    def next(self):
        if not self.position:
            self.order = self.buy(size=self.position_size)
        else:
            current_price = self.dataclose[0]
            if current_price > self.buy_price * (
                1 + self.params.add_on_percentage / 100
            ):
                new_size = self.position_size + int(
                    self.position_size * self.params.add_on_percentage
                )
                self.order = self.buy(size=new_size)
                self.position_size = new_size
            elif current_price < self.buy_price * (
                1 - self.params.stop_loss_percentage / 100
            ):
                self.close()

    def log(self, txt):
        dt = self.datas[0].datetime.date(0)
        print(f"{dt}, {txt}")


if __name__ == "__main__":
    cerebro = bt.Cerebro()
    data = bt.feeds.GenericCSVData(
        dataname="your_data.csv",
        dtformat=("%Y-%m-%d"),
        datetime=0,
        open=1,
        high=2,
        low=3,
        close=4,
        volume=5,
        openinterest=-1,
    )
    cerebro.adddata(data)
    cerebro.addstrategy(LivermorePyramidingStrategy)
    cerebro.broker.setcash(100000.0)
    cerebro.broker.setcommission(commission=0.001)
    print("Starting Portfolio Value: %.2f" % cerebro.broker.getvalue())
    cerebro.run()
    print("Final Portfolio Value: %.2f" % cerebro.broker.getvalue())

"""
金字塔加仓是一种在交易中逐渐增加仓位的策略，以下是一些相关的策略和技巧：
一、策略
固定比例金字塔加仓
设定每次加仓的比例是固定的。例如，初始仓位为 1 手，每次加仓为现有仓位的 50%。这样，随着价格朝着预期方向移动，仓位逐渐增加，但加仓的规模相对递减，形成金字塔形状。
优点：可以在趋势明确时逐步扩大盈利，同时控制风险。
缺点：如果市场反转，可能会导致较大的回撤。
基于技术指标的金字塔加仓
使用技术指标来确定加仓时机。例如，当价格突破重要的移动平均线、趋势线或者达到特定的震荡指标值时进行加仓。
优点：可以利用技术分析来提高加仓的准确性。
缺点：技术指标可能会出现误判，而且不同的指标可能给出不同的信号。
基于价格区间的金字塔加仓
将价格区间划分为几个层次，当价格进入下一个层次时进行加仓。例如，将价格区间分为三个层次，初始仓位在第一个层次建立，当价格上涨一定幅度进入第二个层次时加仓，再上涨进入第三个层次时再次加仓。
优点：可以根据价格的实际走势进行有针对性的加仓。
缺点：需要准确地划分价格区间，否则可能会错过加仓时机或者在不合适的位置加仓。
二、技巧
设置合理的加仓间距
加仓间距不能太近，否则容易在市场波动中频繁加仓，增加成本和风险；也不能太远，否则可能错过趋势中的大部分利润。可以根据市场的波动性和交易品种的特点来确定合适的加仓间距。
结合止损策略
在进行金字塔加仓的同时，一定要设置止损位。当市场反转时，及时止损可以限制损失。止损位可以根据技术分析、资金管理原则或者个人风险承受能力来确定。
控制总仓位
虽然金字塔加仓可以逐步增加仓位，但也要注意控制总仓位，避免过度杠杆化。一般来说，总仓位不宜超过一定比例，例如账户资金的 50% 或 100%。
考虑市场环境
在不同的市场环境下，金字塔加仓的策略可能需要调整。例如，在趋势明显的市场中，可以更积极地加仓；而在震荡市场中，加仓要更加谨慎，以免被市场反复震荡所套牢。
进行模拟交易和回测
在实际应用金字塔加仓策略之前，可以先进行模拟交易和回测，以检验策略的有效性和稳定性。通过回测可以了解策略在不同市场条件下的表现，从而进行优化和调整。
        
        
"""
